﻿// Inner Kit 库
// XML读取类
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类用于读取XML，并将其转化为交互者友好的类。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-14

#pragma once

#include <cstdint>
#include <cstdlib>

#include "common_utils.h"
#include "tinyxml2.h"

namespace ikit {
class XmlNode;
typedef XmlNode* LPXmlNode;

typedef void (*XmlNodeCallback)(const XmlNode& node, void* classOwner, void* itemOwner);

struct XmlValue {
  uint64_t id;       // 一个hash值ID
  char* value;       // 字符串值
  int int_value;     // int值
  float float_value; // 浮点值

  inline std::string GbkValue() const { return ikit::utf8_to_gbk(value); }
};

class XmlNode {
public:
  XmlNode();
  ~XmlNode();

  // 从XML文件中读取
  // TODO：这东西破坏了封装。我得想个办法使tinyxml2不在外面。
  void Read(tinyxml2::XMLElement* xml_node);

  inline uint64_t ID() const { return value_.id; }
  inline const char* Value() const { return value_.value; }
  inline std::string GbkValue() const { return ikit::utf8_to_gbk(value_.value); }
  inline bool Enable() const { return value_.int_value; }
  inline int Int() const { return value_.int_value; }
  inline int Int(int defaultValue) const {
    return value_.int_value == 0 ? defaultValue : value_.int_value;
  }
  inline float Float() const { return value_.float_value; }
  inline float Float(float defaultValue) const {
    return value_.float_value == 0.0f ? defaultValue : value_.float_value;
  }
  inline bool HasChild() const { return children_count_ > 0; }
  inline const XmlNode& Get(int index) const { return children_[index]; }

  const XmlNode& operator[](const char* key) const;
  const XmlNode& Get(const char* key) const;
  const XmlValue& Attr(const char* key) const;

  void Foreach(const char* key, XmlNodeCallback callback, void* classOwner, void* itemOwner) const;

private:
  XmlValue value_;       // 自身的值（ID就包括在里面）
  XmlValue* attributes_; // 属性
  int attributes_count_; // 属性的数量
  XmlNode* children_;    // 子项目
  int children_count_;   // 子项目的数量
};

class XmlReader {

public:
  XmlReader() = default;
  XmlReader(const char* filename);
  ~XmlReader() = default;

  void Load(const char* filename);
  const XmlNode& operator[](const char* key) const;
  const XmlNode& Get(const char* key) const;

private:
  XmlNode root_;
};

} // namespace ikit
